上一篇使用then()和catch()取得 Promise 的成功結果與失敗原因。如果非同步工作只有一步,這種寫法很直接;但需要先取得課程、再讀取詳細資料、最後更新畫面時,就會出現多段連續的then()。
async和await不會取代 Promise,而是提供另一種等待 Promise 的寫法。它能把每一步結果先放進變數,再繼續下一行,閱讀順序會比較接近一般由上往下執行的程式。
先準備一個會取得訊息的非同步函式:
async function getMessage() {
return "讀取完成";
}
const result = getMessage();
console.log(result instanceof Promise); // true
函式內要使用await時,需要在函式前面加上async。async也會讓函式固定回傳 Promise,所以呼叫getMessage()時不會直接得到「讀取完成」,而是先得到一個 Promise。
使用then()取得訊息時,可以這樣寫:
getMessage().then((message) => {
console.log("then 結果:", message);
});
改用await後,可以先等待結果,再使用取得的資料:
async function showMessage() {
const message = await getMessage();
console.log("await 結果:", message);
}
showMessage();

await會暫停目前這個async函式,等右邊的 Promise完成後再往下走,不會讓全部 JavaScript一起停住。這就是使用async的主要原因:讓函式內可以使用await,並讓呼叫端仍然拿到 Promise,知道這個非同步函式何時完成。
目前實作使用一般的script載入 JavaScript,因此不能直接在檔案最外層寫await showMessage()。要把需要等待的流程放進async函式,再呼叫這個函式:
async function runDay21() {
await showMessage();
await loadLessons();
}
runDay21().catch((error) => {
console.error("Day 21 執行失敗:", error.message);
});

如果 JavaScript是用type="module"載入,模組最外層可以使用await;不過這篇沿用前面的一般 script,所以使用runDay21()包住流程。
fetch()用來發送網路請求。收到回應時,先得到Response,還不是已解析的資料:
const response = await fetch(url);
console.log(response.ok);
console.log(response.status);
ok表示狀態碼是否在成功範圍,status則是實際狀態碼。收到 404 或 500 時,fetch()仍可能正常回傳Response,所以要自己檢查:
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}

接著依照回應內容選擇解析方法。json()解析 JSON,text()則取得一般文字,兩者都會回傳 Promise:
const data = await response.json();
// const text = await response.text();

同一份回應內容通常只能讀取一次,不能先呼叫json()後再呼叫text()。如果只是要比較兩種結果,可以先使用response.clone()複製回應。
多個工作互相依賴時,可以逐一await;互不依賴時則使用Promise.all()同時等待:
const [first, second] = await Promise.all([
fetch(firstUrl),
fetch(secondUrl),
]);
